package com.dhl.fin.api.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import com.dhl.fin.api.common.dto.QueryDto;
import com.dhl.fin.api.common.service.CommonService;
import com.dhl.fin.api.common.util.*;
import com.dhl.fin.api.dao.MyStatementDao;
import com.dhl.fin.api.domain.*;
import com.dhl.fin.api.dto.BankStatementRequestDTO;
import com.dhl.fin.api.dto.LastMonthInterestStatementDTO;
import com.dhl.fin.api.enums.BusinessTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author CuiJianbo
 * @since 2025/3/16
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class BankStatementService extends CommonService<BankStatement> {

    @Autowired
    private BankAccountService bankAccountService;

    @Autowired
    private BankCardMoneyReportService bankCardMoneyReportService;

    @Autowired
    private FamilyConfigService familyConfigService;

    @Autowired
    private FamilyService familyService;

    @Autowired
    private FixDepositService fixDepositService;

    @Autowired
    private BusinessRecordService businessRecordService;

    @Autowired
    private MyStatementDao myStatementDao;

    @Override
    public void beforePageQuery(QueryDto pageQuery) throws Exception {

        Integer lastMonth = WebUtil.getIntegerParam("lastMonth");
        if (ObjectUtil.notNull(lastMonth)) {
            DateTime startDate = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.MONTH, -lastMonth);
            pageQuery.getWhereCondition().add("bank_statement.create_time >= '" + DateUtil.getFullTime(startDate) + "'");
        }

    }

    /**
     * 每月计算活期利息更新账户余额
     */
    public void addMonthlyInterestStatement(LastMonthInterestStatementDTO lastMonthInterestStatementDTO) throws Exception {
        long bankAccountId = lastMonthInterestStatementDTO.getBankAccountId();
        BankAccount bankAccount = bankAccountService.get(bankAccountId);
        Double newTotalBalance = bankAccount.getCurrentBalance() + lastMonthInterestStatementDTO.getTotalInterest();
        Double newTotalInterest = bankAccount.getTotalInterestIncome() + lastMonthInterestStatementDTO.getTotalInterest();
        bankAccount.setCurrentBalance(newTotalBalance);
        bankAccount.setTotalInterestIncome(newTotalInterest);

        lastMonthInterestStatementDTO.setNewCurrentBalance(newTotalBalance);
        BankStatement bankStatement = BankStatement.convertToLastMonthInterestStatementDTO(lastMonthInterestStatementDTO);

        //保存流水记录
        saveOrUpdate(bankStatement);

        //更新账户余额和总利息
        bankAccountService.updateBySelective(bankAccount);

    }

    /**
     * 活期账户余额进账流水
     */
    public BankStatement incomeBankStatement(BankStatementRequestDTO bankStatementRequestDTO) throws Exception {
        Double newTotalBalance = bankStatementRequestDTO.getCurrentBalance() + bankStatementRequestDTO.getIncomeMoney();
        BankStatement bankStatement = createOneStatement(bankStatementRequestDTO, newTotalBalance);

        //保存流水记录
        saveOrUpdate(bankStatement);

        //更新账户余额
        bankAccountService.updateFieldById(bankStatementRequestDTO.getBankAccountId(), "currentBalance", newTotalBalance);

        return bankStatement;
    }


    /**
     * 活期账户余额出账流水
     */
    public void outMoneyBankStatement(BankStatementRequestDTO bankStatementRequestDTO) throws Exception {
        Double newTotalBalance = bankStatementRequestDTO.getCurrentBalance() - bankStatementRequestDTO.getIncomeMoney();
        BankStatement bankStatement = createOneStatement(bankStatementRequestDTO, newTotalBalance);

        //保存流水记录
        saveOrUpdate(bankStatement);

        //更新账户余额
        bankAccountService.updateFieldById(bankStatementRequestDTO.getBankAccountId(), "currentBalance", newTotalBalance);

        BusinessRecord businessRecord = new BusinessRecord();
        String actionName = bankStatement.getTypeName().equals(BusinessTypeEnum.FAIR_INVOICE.getName()) ? "扣减" : "取出";
        BeanUtil.copyProperties(bankStatement, businessRecord);
        businessRecord.setBusinessName(bankStatement.getTypeName());
        businessRecord.setBusinessBrief(String.format(actionName + "%.0f元", bankStatement.getMoneyAmount()));
        businessRecord.setBankAccount(bankStatement.getBankAccount());
        businessRecord.setId(null);

        //保存业务记录
        businessRecordService.save(businessRecord);

    }

    private BankStatement createOneStatement(BankStatementRequestDTO bankStatementRequestDTO, Double newTotalBalance) {
        BankStatement bankStatement = new BankStatement();
        BankAccount bankAccount = new BankAccount();
        String typeName = BusinessTypeEnum.getNameByCode(bankStatementRequestDTO.getType());
        Long bankAccountId = bankStatementRequestDTO.getBankAccountId();
        bankAccount.setId(bankAccountId);

        bankStatement.setBankAccount(bankAccount);
        bankStatement.setRemark(bankStatementRequestDTO.getRemark());
        bankStatement.setTag(bankStatementRequestDTO.getTags());
        bankStatement.setMoneyAmount(bankStatementRequestDTO.getIncomeMoney());
        bankStatement.setOperator(bankStatementRequestDTO.getOperator());
        bankStatement.setTypeName(typeName);
        bankStatement.setYear(DateUtil.getCurrentYear());
        bankStatement.setMonth(DateUtil.getCurrentMonth());
        bankStatement.setCurrentBalance(newTotalBalance);

        return bankStatement;
    }

    /**
     * 计算定期存钱的每年利息
     */
    public List<Map> calculateInterest(Double money, String unicode) throws Exception {

        List<FamilyConfig> familyConfigs = familyConfigService.getYearRateConfigs(unicode);
        List<Map> yearRateList = new LinkedList<>();
        if (CollectorUtil.isNoTEmpty(familyConfigs)) {
            for (FamilyConfig familyConfig : familyConfigs) {
                Integer year = Integer.valueOf(familyConfig.getName().replace("yearRate", ""));
                Double rate = Double.valueOf(familyConfig.getValue());
                Double interest = ObjectUtil.isNull(money) ? 0 : money * (rate * 1.0 / 100) * year;
                DateTime expireDate = cn.hutool.core.date.DateUtil.offset(new Date(), DateField.YEAR, year);

                yearRateList.add(MapUtil.builder().add("year", year).add("rate", rate)
                        .add("expireDate", DateUtil.getTime(expireDate))
                        .add("interest", String.format("%.2f", interest)).build());
            }
        }

        return yearRateList;
    }


    /**
     * 办理定期存款业务
     */
    public void fixIncomeBankStatement(BankStatementRequestDTO bankStatementRequestDTO) throws Exception {
        BankStatement bankStatement = createOneStatement(bankStatementRequestDTO, bankStatementRequestDTO.getCurrentBalance());

        //添加固定存款记录
        fixDepositService.addFixDepositRecord(bankStatementRequestDTO);

        BusinessRecord businessRecord = new BusinessRecord();
        BeanUtil.copyProperties(bankStatement, businessRecord);
        businessRecord.setBusinessName(bankStatement.getTypeName());
        businessRecord.setBusinessBrief(String.format("存入%.0f元", bankStatement.getMoneyAmount()));
        businessRecord.setBankAccount(bankStatement.getBankAccount());
        businessRecord.setId(null);

        //保存业务记录
        businessRecordService.save(businessRecord);
    }

    /**
     * 固定存款到期，自动转入账户余额
     */
    public void fixDepositChangeToTimeMoney(FixDeposit fixDeposit) throws Exception {

        BankAccount bankAccount = fixDeposit.getBankAccount();
        Double currentBalance = bankAccount.getCurrentBalance();
        Double newTotalBalance = currentBalance + fixDeposit.getMoneyAmount();

        BankStatement bankStatementMoney = new BankStatement();
        bankStatementMoney.setBankAccount(bankAccount);
        bankStatementMoney.setRemark("定期存款到期，自动转入账户余额");
        bankStatementMoney.setMoneyAmount(fixDeposit.getMoneyAmount());
        bankStatementMoney.setOperator("system");
        bankStatementMoney.setTypeName(BusinessTypeEnum.FIX_MONEY.getName());
        bankStatementMoney.setYear(DateUtil.getCurrentYear());
        bankStatementMoney.setMonth(DateUtil.getCurrentMonth());
        bankStatementMoney.setCurrentBalance(newTotalBalance);


        newTotalBalance = newTotalBalance + fixDeposit.getTotalInterest();
        BankStatement bankStatementInterest = new BankStatement();
        bankStatementInterest.setBankAccount(bankAccount);
        bankStatementInterest.setRemark("定期存款到期，总利息收入转入账户余额");
        bankStatementInterest.setMoneyAmount(fixDeposit.getTotalInterest());
        bankStatementInterest.setOperator("system");
        bankStatementInterest.setTypeName(BusinessTypeEnum.FIX_MONEY_INTEREST.getName());
        bankStatementInterest.setYear(DateUtil.getCurrentYear());
        bankStatementInterest.setMonth(DateUtil.getCurrentMonth());
        bankStatementInterest.setCurrentBalance(newTotalBalance);


        //保存流水记录
        saveOrUpdate(bankStatementMoney);
        saveOrUpdate(bankStatementInterest);

        //更新账户余额
        bankAccountService.updateFieldById(bankAccount.getId(), "currentBalance", newTotalBalance);

    }

    public void fixOut(Long fixDepositId) throws Exception {
        WebUtil.getLoginUser().getUuid();
        Family family = familyService.getLoginUserInfo();
        String penalty = familyConfigService.getPenalty(family.getUnicode());
        FixDeposit fixDeposit = fixDepositService.get(fixDepositId, "bankAccount");
        Map penaltyMap = fixDepositService.calculatePenalty(fixDepositId, family.getUnicode());
        String[] nowDate = DateUtil.getTime(DateUtil.getSysDate()).split("-");
        Double leaveMoney = MapUtil.getDouble(penaltyMap, "leaveMoney");
        Double lostMoney = MapUtil.getDouble(penaltyMap, "lostMoney");
        Double newCurrentBalance = fixDeposit.getBankAccount().getCurrentBalance() + leaveMoney;

        BankStatement bankStatement = new BankStatement();
        bankStatement.setBankAccount(fixDeposit.getBankAccount());
        bankStatement.setTypeName(BusinessTypeEnum.FIX_OUT.getName());
        bankStatement.setMoneyAmount(leaveMoney);
        bankStatement.setCurrentBalance(newCurrentBalance);
        bankStatement.setYear(DateUtil.getCurrentYear());
        bankStatement.setMonth(DateUtil.getCurrentMonth());
        bankStatement.setOperator(family.getMemberAlias());
        bankStatement.setRemark(String.format("定期存款%.2f元，扣除违约金%.2f元（%s%%的违约金），剩余%.2f元转入活期账户余额",
                        fixDeposit.getMoneyAmount(),
                        lostMoney,
                        penalty,
                        leaveMoney
                )
        );

        //保存流水记录
        saveOrUpdate(bankStatement);

        //更新数据为已完成
        fixDepositService.updateFieldById(fixDepositId, "finished", 1);

        //更新账户余额
        bankAccountService.updateFieldById(fixDeposit.getBankAccount().getId(), "currentBalance", newCurrentBalance);

        BusinessRecord businessRecord = new BusinessRecord();
        BeanUtil.copyProperties(bankStatement, businessRecord);
        businessRecord.setBusinessName(bankStatement.getTypeName());
        businessRecord.setBusinessBrief(String.format("提前取出定期存款%.2f元", bankStatement.getMoneyAmount()));
        businessRecord.setBankAccount(bankStatement.getBankAccount());
        businessRecord.setId(null);

        //保存业务记录
        businessRecordService.save(businessRecord);
    }


    public void timeToFix(BankStatementRequestDTO bankStatementRequestDTO) throws Exception {
        Double newTotalBalance = bankStatementRequestDTO.getCurrentBalance() - bankStatementRequestDTO.getIncomeMoney();
        BankStatement bankStatement = createOneStatement(bankStatementRequestDTO, newTotalBalance);

        //添加流水记录
        saveOrUpdate(bankStatement);

        //添加固定存款记录
        fixDepositService.addFixDepositRecord(bankStatementRequestDTO);

        //更新账户余额
        bankAccountService.updateFieldById(bankStatementRequestDTO.getBankAccountId(), "currentBalance", newTotalBalance);

        BusinessRecord businessRecord = new BusinessRecord();
        BeanUtil.copyProperties(bankStatement, businessRecord);
        businessRecord.setBusinessName(bankStatement.getTypeName());
        businessRecord.setBusinessBrief(String.format("转入%.0f元", bankStatement.getMoneyAmount()));
        businessRecord.setBankAccount(bankStatement.getBankAccount());
        businessRecord.setId(null);

        //保存业务记录
        businessRecordService.save(businessRecord);
    }


    public Map calculateTotalSummary(Long bankAccountId, Integer lastMonth) throws Exception {
        Date startDate = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.MONTH, -lastMonth);

        List<BankStatement> bankStatements = select(QueryDto.builder()
                .available()
                .addJoinDomain("bankAccount")
                .addWhere("bank_account.id='" + bankAccountId + "'")
                .addWhere("type_name != '" + BusinessTypeEnum.FIX_MONEY.getName() + "'")
                .addWhere("type_name != '" + BusinessTypeEnum.FIX_INCOME.getName() + "'")
                .addWhere("bank_statement.create_time > '" + DateUtil.getFullTime(startDate) + "'")
                .build());

        Double totalIncome = bankStatements.stream()
                .filter(p -> BusinessTypeEnum.getByName(p.getTypeName()).getPositive())
                .map(p -> p.getMoneyAmount())
                .reduce(0.0, Double::sum);

        Double totalOut = bankStatements.stream()
                .filter(p -> !BusinessTypeEnum.getByName(p.getTypeName()).getPositive())
                .map(p -> p.getMoneyAmount())
                .reduce(0.0, Double::sum);

        Double totalInterest = bankStatements.stream()
                .filter(p -> p.getTypeName().equals("活期利息") || p.getTypeName().equals("定期存款利息"))
                .map(p -> p.getMoneyAmount())
                .reduce(0.0, Double::sum);

        return MapUtil.builder()
                .add("totalIncome", String.format("%,.2f", totalIncome))
                .add("totalOut", String.format("%,.2f", totalOut))
                .add("totalInterest", String.format("%,.2f", totalInterest))
                .build();

    }

    @Override
    public void afterGet(BankStatement domain) {

        BusinessTypeEnum businessTypeEnum = BusinessTypeEnum.getByName(domain.getTypeName());

        domain.setMoneyAmount(businessTypeEnum.getPositive() ? domain.getMoneyAmount() : -domain.getMoneyAmount());

    }

    public Map dataAnalysis(Long bankAccountId) throws Exception {
        Date startMonthDate = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.MONTH, -12);
        Date startYearDate = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.YEAR, -10);
        Integer year = cn.hutool.core.date.DateUtil.year(startYearDate);
        String startDateStr = DateUtil.getShortTime(startMonthDate) + "-01";
        String startYearStr = year + "-01-01";
        int yearLength = 5;
        int monthLength = 12;

        String[] monthData = new String[monthLength];
        String[] yearData = new String[yearLength];
        String[] incomeMonthData = new String[monthLength];
        String[] interestMonthData = new String[monthLength];

        String[] incomeYearData = new String[yearLength];
        String[] interestYearData = new String[yearLength];


        List<BankCardMoneyReport> totalReportMonth = bankCardMoneyReportService.selectTotalIncomeByMonth(bankAccountId, startDateStr);
        List<BankCardMoneyReport> totalReportYear = myStatementDao.selectReportDataByYear(bankAccountId, startYearStr);


        // 生成每月数据
        for (int i = 0; i < monthLength; i++) {
            Date date = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.MONTH, -i);
            String dateStr = DateUtil.getShortTime(date);
            int monthNumber = cn.hutool.core.date.DateUtil.month(date);
            monthData[monthLength - 1 - i] = ++monthNumber + "月";

            BankCardMoneyReport incomeData = totalReportMonth.stream().filter(p ->
                            p.getYearMonthDate().equals(dateStr)
                    ).findFirst()
                    .orElse(null);

            if (ObjectUtil.notNull(incomeData)) {
                Double currentBalance = incomeData.getCurrentBalance();
                Double interestIncome = incomeData.getCurrentInterestIncome();
                incomeMonthData[monthLength - 1 - i] = String.format("%.2f", currentBalance);
                interestMonthData[monthLength - 1 - i] = String.format("%.2f", interestIncome);
            } else {
                incomeMonthData[monthLength - 1 - i] = "0";
                interestMonthData[monthLength - 1 - i] = "0";
            }
        }


        // 生成每年数据
        for (int i = 0; i < yearLength; i++) {
            Date date = cn.hutool.core.date.DateUtil.offset(DateUtil.getSysDate(), DateField.YEAR, -i);
            Integer yearNumber = cn.hutool.core.date.DateUtil.year(date);
            yearData[yearLength - 1 - i] = yearNumber + "年";

            BankCardMoneyReport incomeData = totalReportYear.stream().filter(p ->
                            p.getYear().equals(yearNumber)).findFirst()
                    .orElse(null);

            if (ObjectUtil.notNull(incomeData)) {
                Double currentBalance = incomeData.getCurrentBalance();
                Double interestIncome = incomeData.getCurrentInterestIncome();
                incomeYearData[yearLength - 1 - i] = String.format("%.2f", currentBalance);
                interestYearData[yearLength - 1 - i] = String.format("%.2f", interestIncome);
            } else {
                incomeYearData[yearLength - 1 - i] = "0";
                interestYearData[yearLength - 1 - i] = "0";
            }
        }

        Map monthMap = MapUtil.builder()
                .add("totalIncome", changeMoreDetailData(incomeMonthData))
                .add("totalInterest", changeMoreDetailData(interestMonthData))
                .build();

        Map yearMap = MapUtil.builder()
                .add("totalIncome", changeMoreDetailData(incomeYearData))
                .add("totalInterest", changeMoreDetailData(interestYearData))
                .build();

        Map result = MapUtil.builder()
                .add("month", monthMap)
                .add("year", yearMap)
                .add("monthData", monthData)
                .add("yearData", yearData)
                .build();

        return result;

    }

    private Map changeMoreDetailData(String[] data) {
        Double maxDouble = Arrays.stream(data)
                .map(Double::parseDouble)
                .max(Double::compareTo).get();

        Double minDouble = Arrays.stream(data)
                .map(Double::parseDouble)
                .min(Double::compareTo).get();

        Double interval = (maxDouble - minDouble) / 5;
        int intervalz = (interval.intValue() / 10 + 1) * 10;
        int minN = (minDouble.intValue() / intervalz);
        int maxN = (maxDouble.intValue() / intervalz) + 1;
        int minValue = minN * intervalz;
        int maxValue = maxN * intervalz;

        return MapUtil.builder()
                .add("data", data)
                .add("interval", intervalz)
                .add("max", maxValue)
                .add("min", minValue)
                .build();
    }


    public List<String> getBankStatementYearMonth(DateTime startTime, String familyCode) {
        return myStatementDao.queryBankStatementDateTime(DateUtil.getTime(startTime), familyCode);
    }

    public List<BankStatement> queryBankStatement(String yearmonth, Long bankAccountId) throws Exception {
        return select(QueryDto.builder()
                .available()
                .addWhere("bank_account_id = " + bankAccountId)
                .addWhere("DATE_FORMAT(create_time, '%Y-%m') = '" + yearmonth + "'")
                .build());
    }


}
